package com.alan.api.core;

import com.alan.api.common.ApiException;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Alan
 * @create 2018-01-10 22:02
 * @desc test
 **/
@Component
@Slf4j
public class ApiGatewayHand implements InitializingBean,ApplicationContextAware{

    private static String PARAMS = "params";
    private static String METHOD = "method";

    private ApiStore apiStore;

    final ParameterNameDiscoverer parameterUtil;

    public ApiGatewayHand() {
        // spring 提供的，用来获取方法中的参数名
        parameterUtil = new LocalVariableTableParameterNameDiscoverer();
    }

    public Object handle(HttpServletRequest request) throws ApiException, InvocationTargetException, IllegalAccessException {
        // 参数
        String params = request.getParameter(PARAMS);
        // apiName
        String method = request.getParameter(METHOD);
        ApiStore.ApiRunnable apiRun = null;
        // 验证系统参数
        apiRun = sysParamValdate(request);
        log.info("请求接口={"+method+"} 参数="+params+"");
        // 构建参数
        Object[] args = buildParams(apiRun,params,request);
        // 方法执行
        Object obj = apiRun.run(args);
        log.info("返回接口={"+method+"} 数据="+JSON.toJSONString(obj)+"");
        return obj;
    }

    private Object[] buildParams(ApiStore.ApiRunnable run, String paramJson, HttpServletRequest request) throws ApiException {
        Map<String,Object> map = null;
        map = JSON.parseObject(paramJson,Map.class);
        if( map == null ){
            map = new HashMap<String, Object>();
        }
        Method method = run.getTargetMethod();
        List<String> paramNames = Arrays.asList(parameterUtil.getParameterNames(method));
        // goods,id
        Class<?>[] paramTypes = method.getParameterTypes();
        APIMapping apiMapping = method.getAnnotation(APIMapping.class);
        boolean isChekc = apiMapping.isCheck();
        // 多余参数检查
        for (Map.Entry<String,Object> m: map.entrySet()) {
            if(!paramNames.contains(m.getKey())){
                throw new ApiException("调用失败：接口不存在'"+m.getKey()+"'参数");
            }
        }
        // 空检查（必传）
        if(isChekc){
            for (String item:paramNames) {
                if(!map.keySet().contains(item)){
                    throw new ApiException("调用失败：'"+item+"'参数不能为空");
                }
            }
        }
        Object[] args = new Object[paramTypes.length];
        for (int i = 0; i < paramTypes.length; i++) {
            // HttpServletRequest 对象处理
            if(paramTypes[i].isAssignableFrom(HttpServletRequest.class)){
                args[i] = request;
            }else if(map.containsKey(paramNames.get(i))){
                try {
                    args[i] = JSON.parseObject(JSON.toJSONString(map.get(paramNames.get(i))),paramTypes[i]);
                } catch (Exception e){
                    throw new ApiException("调用失败：指定参数格式错误名值错误'"+paramNames.get(i)+"'参数"+e.getMessage());
                }
            }else{
                // 其他参数为null
                args[i] = null;
            }
        }
        return args;
    }

    private ApiStore.ApiRunnable sysParamValdate(HttpServletRequest request) throws ApiException {
        String apiName = request.getParameter(METHOD);
        String json = request.getParameter(PARAMS);
        ApiStore.ApiRunnable api;
        if(apiName == null || apiName.trim().equals("")){
            throw new ApiException("调用失败：参数'method'为空");
        }else if(json == null){
            throw new ApiException("调用失败：参数'params'为空");
        }else if( (api = apiStore.findApiRunnable(apiName)) == null ){
            throw new ApiException("调用失败：指定API不存在，API:"+apiName);
        }
        // 可加签名
        return  api;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        apiStore.loadApiFromSpringBeans();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        apiStore = new ApiStore(applicationContext);
    }
}
